//    Copyright (C) Kherty.  All rights reserved.
using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Diagnostics;
using System.Diagnostics.Contracts;
using System.Windows;
using OpenLS.Drawing;

namespace OpenLS.Spreadsheet.UI.Internals
{
    public abstract class CellControl : FrameworkElement
    {
        public static readonly DependencyProperty ColorSchemeProperty =
            DependencyProperty.Register("ColorScheme", typeof(ColorScheme), typeof(CellControl),
                                        new FrameworkPropertyMetadata(null,
                                                                      FrameworkPropertyMetadataOptions.AffectsRender,
                                                                      (sender, args) => ((CellControl)sender).OnColorSchemeChanged()));

        //\\name
        public Worksheet Worksheet
        {
            get
            {
                var worksheetPane = WorksheetPane;
                return worksheetPane == null ? null : worksheetPane.Worksheet;
            }
        }

        public ColorScheme ColorScheme
        {
            get { return (ColorScheme)GetValue(ColorSchemeProperty); }
            set { SetValue(ColorSchemeProperty, value); }
        }

        public IWorksheetFragment WorksheetPane
        {
            get { return (IWorksheetFragment)GetValue(WorksheetPaneProperty); }
            set { SetValue(WorksheetPaneProperty, value); }
        }

        // Using a DependencyProperty as the backing store for ColorScheme.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty WorksheetPaneProperty =
            DependencyProperty.Register("WorksheetPane", typeof(IWorksheetFragment), typeof(CellControl)
                                        ,
                                        new UIPropertyMetadata(null,
                                                               (d, e) => ((CellControl)d).OnWorksheetPaneChanged(d, e)));

        internal virtual void OnColorSchemeChanged()
        {
            InvalidateVisual();
        }


        protected virtual IEnumerable<Cell> GetCells()
        {
            if (WorksheetPane == null)
                yield break;
            foreach (Cell c in WorksheetPane.GetCells())
                yield return c;
        }

        private Rect getRelativeRect(int col1, int row1, int col2, int row2)
        {
            Contract.Requires(col2 >= 0);
            Contract.Requires(col2 <= Column.MaxIndex + 1);
            Contract.Requires(row2 >= 0);
            Contract.Requires(row2 <= Row.MaxIndex + 1);
            Contract.Assert(Row.ValidIndex(row1));
            Contract.Assert(Column.ValidIndex(col1));
            return new Rect(new Point(Worksheet.Columns.GetOffset(col1) - Worksheet.Columns.GetOffset(WorksheetPane.Left),
                                      Worksheet.Rows.GetOffset(row1) - Worksheet.Rows.GetOffset(WorksheetPane.Top)),
                            new Point(Worksheet.Columns.GetOffset(col2) - Worksheet.Columns.GetOffset(WorksheetPane.Left),
                                      Worksheet.Rows.GetOffset(row2) - Worksheet.Rows.GetOffset(WorksheetPane.Top)));
        }
        private Rect getAbsoluteRect(int col1, int row1, int col2, int row2)
        {
            Contract.Requires(col2 >= 0);
            Contract.Requires(col2 <= Column.MaxIndex + 1);
            Contract.Requires(row2 >= 0);
            Contract.Requires(row2 <= Row.MaxIndex + 1);
            Contract.Assert(Row.ValidIndex(row1));
            Contract.Assert(Column.ValidIndex(col1));
            return new Rect(new Point(Worksheet.Columns.GetOffset(col1),
                                      Worksheet.Rows.GetOffset(row1)) ,
                            new Point(Worksheet.Columns.GetOffset(col2) ,
                                      Worksheet.Rows.GetOffset(row2) ));
        }
        internal Point GetRelativeLocation(CellAddress address)
        {
            return new Point(Worksheet.Columns.GetOffset(address.Column) - Worksheet.Columns.GetOffset(WorksheetPane.Left),
                                                  Worksheet.Rows.GetOffset(address.Row) - Worksheet.Rows.GetOffset(WorksheetPane.Top))
                                                  ;
        }
        internal Point GetAbsoluteLocation(CellAddress address)
        {
            return new Point(Worksheet.Columns.GetOffset(address.Column) ,
                                                  Worksheet.Rows.GetOffset(address.Row))
                                                  ;
        }

        internal Rect GetRelativeRect(Cell c)
        {
            return getRelativeRect(c.Column.Index, c.Row.Index, c.Column.Index + c.ColumnSpan,
                           c.Row.Index + c.RowSpan);
        }
        protected Rect GetRect(CellAddress c)
        {
            return getRelativeRect(c.Column, c.Row, c.Column + 1,
                           c.Row + 1);
        }
        protected Rect GetRect(Column column)
        {
            return new Rect(
                new Point(Worksheet.Columns.GetOffset(column.Index) - Worksheet.Columns.GetOffset(WorksheetPane.Left),
                          0),
                new Point(
                    Worksheet.Columns.GetOffset(column.Index + 1) - Worksheet.Columns.GetOffset(WorksheetPane.Left),
                    ActualHeight));

        }
        protected Rect GetRect(Row row)
        {
            return new Rect(new Point(
                     0,
                     Worksheet.Rows.GetOffset(row.Index) -
                     Worksheet.Rows.GetOffset(WorksheetPane.Top)
                     ),
                 new Point(ActualWidth,
                           Worksheet.Rows.GetOffset(row.Index + 1) -
                           Worksheet.Rows.GetOffset(WorksheetPane.Top)
                     ));

        }

        internal virtual void OnWorksheetPaneChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            var value = e.NewValue as WorksheetPane;
            var oldValue = e.OldValue as WorksheetPane;
            if (oldValue != null)
            {
               // oldValue.Cells.CollectionChanged -= OnCellCollectionChanged;
               // foreach (Cell cell in oldValue.Cells)
               // {
               //     cell.PropertyChanged -= OnCellPropertyChanged;
               // }
                foreach (Row row in oldValue.Rows)
                    row.PropertyChanged -= OnCellPropertyChanged;
                foreach (Column column in oldValue.Columns)
                    column.PropertyChanged -= OnCellPropertyChanged;
                oldValue.PropertyChanged -= onWorksheetViewPropertyChanged;
                oldValue.Worksheet.LayoutChanged -= worksheetLayoutChanged;
            }
            if (value == null)
                return;
            value.Width = ActualWidth;
            value.Height = ActualHeight;
           // value.Cells.CollectionChanged += OnCellCollectionChanged;
            value.PropertyChanged += onWorksheetViewPropertyChanged;
          //  foreach (Cell cell in value.Cells)
          //  {
            //    cell.PropertyChanged += OnCellPropertyChanged;
           // }
            value.Rows.CollectionChanged += OnCellCollectionChanged;
            value.Columns.CollectionChanged += OnCellCollectionChanged;
            foreach (Column column in value.Columns)
                column.PropertyChanged += OnCellPropertyChanged;
            foreach (Row row in value.Rows)
                row.PropertyChanged += OnCellPropertyChanged;
            value.Worksheet.LayoutChanged += worksheetLayoutChanged;
            InvalidateSelf(null);
        }

        private void worksheetLayoutChanged(object sender, EventArgs e)
        {
            InvalidateSelf(null);
        }

        protected virtual void OnCellCollectionChanged(object sender, NotifyCollectionChangedEventArgs arg)
        {
            InvalidateSelf(null);
            if (arg.OldItems != null)
                foreach (INotifyPropertyChanged c in arg.OldItems)
                    c.PropertyChanged -= (OnCellPropertyChanged);
            if (arg.NewItems != null)
                foreach (INotifyPropertyChanged c in arg.NewItems)
                    c.PropertyChanged += (OnCellPropertyChanged);
        }

        protected virtual void OnCellPropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            if (IsRenderProperty(e.PropertyName))
                InvalidateSelf(null);
        }

        protected abstract bool IsRenderProperty(string name);

        private void onWorksheetViewPropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            InvalidateSelf(null);
        }

        internal virtual void InvalidateSelf(Dimension dimension)
        {
            InvalidateVisual();
        }

        internal Rect GetAbsoluteRect(Cell cell)
        {
            return getAbsoluteRect(cell.Column.Index, cell.Row.Index, cell.Column.Index + cell.ColumnSpan,
                           cell.Row.Index + cell.RowSpan);
        }
    }
}